/*
  Name: DeadProxyNetworkHandler.cpp
  Copyright: 
  Author: Carl Chatfield
  Date: 19/01/08 15:59
  Description: Manages network events
*/

#include "DeadProxyApp.h"
#include "winsock.h"

int NetworkHandler::InitWinsock()
{
    WORD sockVersion;
    WSADATA wsaData;
    sockVersion = MAKEWORD(2, 2); // Newest Version Of Winsock
    WSAStartup(sockVersion, &wsaData);
    return 0;
}

int NetworkHandler::InitServers()
{
    //Should be in config
    int nPort = 1337;
    int gPort = 7331;
    //
    int nret;
    // Login Server Start
    // Create Listening Socket
    LoginListenSocket = socket(AF_INET,         // Go over TCP/IP
                               SOCK_STREAM,     // This is a stream-oriented socket
                               IPPROTO_TCP);    // Use TCP rather than UDP
    // Set Socket into Async mode
    WSAAsyncSelect(LoginListenSocket, (HWND)app->frame->GetHandle(), WM_WSAASYNC, FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE);
    if (LoginListenSocket == INVALID_SOCKET)
    {
        nret = WSAGetLastError();               // Get a more detailed error
        return nret;
    }
    // Use a SOCKADDR_IN struct to fill in address information
    SOCKADDR_IN serverInfo;
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_addr.s_addr = INADDR_ANY;    // Since this socket is listening for connections, any local address will do
    serverInfo.sin_port = htons(nPort);         // Convert integer nPort to network-byte order and insert into the port field

    // Bind the socket to our local server address
    nret = bind(LoginListenSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
    if (nret == SOCKET_ERROR)
    {
        nret = WSAGetLastError();
        return nret;
    }
    // Make the socket listen
    nret = listen(LoginListenSocket, 10); // Up to 10 connections may wait at any
    if (nret == SOCKET_ERROR)
    {
        nret = WSAGetLastError();
        return nret;
    }
    //Login Server End
    
    // Game Server Start
    // Create Listening Socket
    GameListenSocket = socket(AF_INET,         // Go over TCP/IP
                               SOCK_STREAM,     // This is a stream-oriented socket
                               IPPROTO_TCP);    // Use TCP rather than UDP
    // Set Socket into Async mode
    WSAAsyncSelect(GameListenSocket, (HWND)app->frame->GetHandle(), WM_WSAASYNC, FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE);
    if (GameListenSocket == INVALID_SOCKET)
    {
        nret = WSAGetLastError();               // Get a more detailed error
        return nret;
    }
    // Use a SOCKADDR_IN struct to fill in address information
    //SOCKADDR_IN serverInfo;
    //serverInfo;
    serverInfo.sin_family = AF_INET;
    serverInfo.sin_addr.s_addr = INADDR_ANY;    // Since this socket is listening for connections, any local address will do
    serverInfo.sin_port = htons(gPort);         // Convert integer gPort to network-byte order and insert into the port field

    // Bind the socket to our local server address
    nret = bind(GameListenSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr));
    if (nret == SOCKET_ERROR)
    {
        nret = WSAGetLastError();
        return nret;
    }
    // Make the socket listen
    nret = listen(GameListenSocket, 10); // Up to 10 connections may wait at any
    if (nret == SOCKET_ERROR)
    {
        nret = WSAGetLastError();
        return nret;
    }
    //Game Server End

    return 0;
}

void NetworkHandler::Message(WPARAM wParam, LPARAM lParam)
{
    ClientEvent * ClientEvt;
    int i;
    switch(WSAGETSELECTEVENT(lParam))
    {
        case FD_ACCEPT:
            if(WSAGETSELECTERROR(lParam))
            {
                return;
            }
            if(wParam == LoginListenSocket)
            {
                SOCKET ToClientSocket = accept(LoginListenSocket, NULL, NULL);
                int i = -1;
                bool SingleClientFound = false;
                bool Fail = false;
                //get connecting client
                //at this point it is uncertain whether the status will be 2 or 3
                //so check for both. Both indicate connecting client
                if(app->CManager->GetClientWithConnectStatus(2, &i) == 1)
                {
                    SingleClientFound = true;
                } else {
                    if(app->CManager->GetClientWithConnectStatus(3, &i) == 1)
                    {
                        SingleClientFound = true;
                    }
                }
                if(SingleClientFound)
                {
                    LPHOSTENT hostEntry;
                    // Now we have to connect to the login server; This must be done in the main thread to
                    // Ensure that we store the Socket in SocketInfos before any more events arrive
                    hostEntry = gethostbyname("login01.tibia.com"); // Specifying the server by its name;
                    if (!hostEntry)
                    {
                        return;
                    }
                    // Create the socket
                    SOCKET ToServerSocket;
                    ToServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                    if (ToServerSocket == INVALID_SOCKET)
                    {
                        return;
                    }
                    // Put into Async mode, this is a client socket
                    WSAAsyncSelect(ToServerSocket, (HWND)app->frame->GetHandle(), WM_WSAASYNC, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);
                    // Fill a SOCKADDR_IN struct with address information
                    SOCKADDR_IN serverInfo;
                    serverInfo.sin_family = AF_INET;
                    serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
                    serverInfo.sin_port = htons(7171); // Change to network-byte order and
                    // Connect to the server
                    if (connect(ToServerSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)) == SOCKET_ERROR)
                    {
                        if(WSAGetLastError() != WSAEWOULDBLOCK)
                        return;
                    }
                    //Add Sockets to SocketInfos
                    SocketInfos->AddSocketInfo(ToClientSocket, i);
                    SocketInfos->AddSocketInfo(ToServerSocket, i);
                    //Post Event To Client
                    ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                    ClientEvt->SetParam1(0); //Instead of wParam, we send 0 for LoginServer and 1 for Game server for accept
                    ClientEvt->SetParam2(lParam);
                    ClientEvt->SetParam3((int)ToClientSocket); //Store the ToClientSocket socket in param3
                    ClientEvt->SetParam4((int)ToServerSocket); //Store the ToServerSocket socket in param4
                    app->CManager->PostEventToClient(i, ClientEvt);
                } else {
                    closesocket(ToClientSocket);
                }
                if(ToClientSocket == INVALID_SOCKET)
                {
                    return;
                }
            }
            if(wParam == GameListenSocket)
            {
                SOCKET ToClientSocket = accept(GameListenSocket, NULL, NULL);
                int i = -1;
                bool SingleClientFound = false;
                bool Fail = false;
                //get connecting client
                //at this point it is uncertain whether the status will be 5 or 6
                //so check for both. Both indicate connecting client
                if(app->CManager->GetClientWithConnectStatus(5, &i) == 1)
                {
                    SingleClientFound = true;
                } else {
                    if(app->CManager->GetClientWithConnectStatus(6, &i) == 1)
                    {
                        SingleClientFound = true;
                    }
                }
                if(SingleClientFound)
                {
                    int ip;
                    short port;
                    app->CManager->GetConnectingCharacterInfo(i, &ip, &port);
                    //convert ip into an address
                    struct in_addr addr;
                    addr.s_addr = ip;
                    char ipstring[16];
                    strcpy(ipstring, inet_ntoa(addr));

                    LPHOSTENT hostEntry;
                    // Now we have to connect to the login server; This must be done in the main thread to
                    // Ensure that we store the Socket in SocketInfos before any more events arrive
                    hostEntry = gethostbyname(ipstring); // Specifying the server by its name;
                    if (!hostEntry)
                    {
                        return;
                    }
                    // Create the socket
                    SOCKET ToServerSocket;
                    ToServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
                    if (ToServerSocket == INVALID_SOCKET)
                    {
                        return;
                    }
                    // Put into Async mode, this is a client socket
                    WSAAsyncSelect(ToServerSocket, (HWND)app->frame->GetHandle(), WM_WSAASYNC, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE);
                    // Fill a SOCKADDR_IN struct with address information
                    SOCKADDR_IN serverInfo;
                    serverInfo.sin_family = AF_INET;
                    serverInfo.sin_addr = *((LPIN_ADDR)*hostEntry->h_addr_list);
                    serverInfo.sin_port = htons(port); // Change to network-byte order and
                    // Connect to the server
                    if (connect(ToServerSocket, (LPSOCKADDR)&serverInfo, sizeof(struct sockaddr)) == SOCKET_ERROR)
                    {
                        if(WSAGetLastError() != WSAEWOULDBLOCK)
                        {
                            return;
                        }
                    }
                    //Add Sockets to SocketInfos
                    SocketInfos->AddSocketInfo(ToClientSocket, i);
                    SocketInfos->AddSocketInfo(ToServerSocket, i);
                    
                    //Post Event To Client
                    ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                    ClientEvt->SetParam1(1); //Instead of wParam, we send 0 for LoginServer and 1 for Game server for accept
                    ClientEvt->SetParam2(lParam);
                    ClientEvt->SetParam3((int)ToClientSocket); //Store the ToClientSocket socket in param3
                    ClientEvt->SetParam4((int)ToServerSocket); //Store the ToServerSocket socket in param4
                    app->CManager->PostEventToClient(i, ClientEvt);
                } else {
                    closesocket(ToClientSocket);
                }
                if(ToClientSocket == INVALID_SOCKET)
                {
                    return;
                }

            }  
            break;
            case FD_READ:
                //Note there is no error checking here, it is to be handled by the client
                i = SocketInfos->GetClient(wParam); //find client which owns the socket
                ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                ClientEvt->SetParam1(wParam);
                ClientEvt->SetParam2(lParam);
                app->CManager->PostEventToClient(i, ClientEvt); //post our event to the client
                //output = new wxString;
                //output->Printf("read %i", wParam);
                //app->LogMessage(output);
                break;
            case FD_WRITE:
                //Note there is no error checking here, it is to be handled by the client
                i = SocketInfos->GetClient(wParam); //find client which owns the socket
                ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                ClientEvt->SetParam1(wParam);
                ClientEvt->SetParam2(lParam);
                app->CManager->PostEventToClient(i, ClientEvt); //post our event to the client
                //output = new wxString;
                //output->Printf("write %i", wParam);
                //app->LogMessage(output);
                break;
            case FD_CONNECT:
                //output = new wxString;
                //output->Printf("connect%i", wParam);
                //app->LogMessage(output);
                //Note there is no error checking here, it is to be handled by the client
                i = SocketInfos->GetClient(wParam); //find client which owns the socket
                ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                ClientEvt->SetParam1(wParam);
                ClientEvt->SetParam2(lParam);
                app->CManager->PostEventToClient(i, ClientEvt); //post our event to the client
                break;
            case FD_CLOSE:
                //Note there is no error checking here, it is to be handled by the client
                i = SocketInfos->GetClient(wParam); //find client which owns the socket
                SocketInfos->RemoveSocketInfo(wParam); //remove old socket so it is nolonger matched to events
                ClientEvt = new ClientEvent(CE_NETEVENT); //set id = 10, it is a network event
                ClientEvt->SetParam1(wParam);
                ClientEvt->SetParam2(lParam);
                app->CManager->PostEventToClient(i, ClientEvt); //post our event to the client
                break;
    }
}

NetworkHandler::NetworkHandler()
{
    SocketInfos = new SocketInfoHandler();
}
            
            
